﻿using HelixToolkit.Wpf.SharpDX;
using HelixToolkit.Wpf.SharpDX.Assimp;
using HelixToolkit.Wpf.SharpDX.Model.Scene;
using Microsoft.Win32;
using SharpDX;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using X.WPF.Controls.Constants;
using X.WPF.Controls.Enums;
using X.WPF.Controls.EventArgs.Point3DCanvasArgs;
using X.WPF.Controls.EventArgs.ThreeDArgs;
using X.WPF.Controls.Extends;
using X.WPF.Controls.Models;
using X.WPF.Controls.Services;
using X.WPF.Controls.Visual3Ds;
using Color = System.Windows.Media.Color;
using HitTestResult = HelixToolkit.Wpf.SharpDX.HitTestResult;
using MeshGeometry3D = HelixToolkit.Wpf.SharpDX.MeshGeometry3D;
using PerspectiveCamera = HelixToolkit.Wpf.SharpDX.PerspectiveCamera;
using Point = System.Windows.Point;

namespace X.WPF.Controls.UserControls
{
    /// <summary>
    /// 3D点画布组件
    /// </summary>
    public partial class Point3DCanvas : UserControl
    {
        #region # 字段及构造器

        /// <summary>
        /// 选中的3D元素
        /// </summary>
        private Element3D _selectedElement3D;

        /// <summary>
        /// 临时点
        /// </summary>
        private PointsVisual3D _tempPointsVisual3D;

        /// <summary>
        /// 是否正在编辑
        /// </summary>
        private bool _edit;

        /// <summary>
        /// 第一个3D元素索引
        /// </summary>
        private readonly int _firstElement3DIndex;

        /// <summary>
        /// 3D数模根节点
        /// </summary>
        private SceneNode _sceneRoot;

        /// <summary>
        /// 默认相机
        /// </summary>
        private readonly ThreeDCamera _defaultCamera;

        /// <summary>
        /// 首次渲染状态
        /// </summary>
        private bool _isFirstRender;

        /// <summary>
        /// 静态构造器
        /// </summary>
        static Point3DCanvas()
        {
            // 注册依赖属性
            Point3DCanvas.MouseLocationProperty = DependencyProperty.Register(nameof(Point3DCanvas.MouseLocation), typeof(string), typeof(Point3DCanvas), new PropertyMetadata(""));
            Point3DCanvas.FilePathProperty = DependencyProperty.Register(nameof(Point3DCanvas.FilePath), typeof(string), typeof(Point3DCanvas), new PropertyMetadata(string.Empty, Point3DCanvas.OnFilePathChanged));
            Point3DCanvas.ColorProperty = DependencyProperty.Register(nameof(Point3DCanvas.Color), typeof(Color), typeof(Point3DCanvas), new PropertyMetadata(Colors.DarkGray, Point3DCanvas.OnFilePathChanged));
            Point3DCanvas.UseModeProperty = DependencyProperty.Register(nameof(Point3DCanvas.UseMode), typeof(CanvasUseMode), typeof(Point3DCanvas), new PropertyMetadata(CanvasUseMode.Nomal, Point3DCanvas.OnUseModelChanged));
            Point3DCanvas.PointRadiusProperty = DependencyProperty.Register(nameof(Point3DCanvas.PointRadius), typeof(double), typeof(Point3DCanvas), new PropertyMetadata(10d, Point3DCanvas.OnPointRadiusChanged));
            Point3DCanvas.PointsProperty = DependencyProperty.Register(nameof(Point3DCanvas.Points), typeof(ObservableCollection<Point3DInfo>), typeof(Point3DCanvas), new PropertyMetadata(new ObservableCollection<Point3DInfo>(), Point3DCanvas.OnPointsChanged));
            Point3DCanvas.OperationModeProperty = DependencyProperty.Register(nameof(Point3DCanvas.OperationMode), typeof(CanvasOperatorMode), typeof(Point3DCanvas), new PropertyMetadata(CanvasOperatorMode.Nomal, Point3DCanvas.OnOperatorModelChanged));
            Point3DCanvas.PointOptimizeModeProperty = DependencyProperty.Register(nameof(Point3DCanvas.PointOptimizeMode), typeof(Point3DOptimizeMode), typeof(Point3DCanvas), new PropertyMetadata(Point3DOptimizeMode.None, Point3DCanvas.OnPoint3DOptimizeModeChanged));
            Point3DCanvas.EffectsManagerProperty = DependencyProperty.Register(nameof(Point3DCanvas.EffectsManager), typeof(IEffectsManager), typeof(Point3DCanvas), new PropertyMetadata(new DefaultEffectsManager()));
            Point3DCanvas.IsRotationEnabledProperty = DependencyProperty.Register(nameof(Point3DCanvas.IsRotationEnabled), typeof(bool), typeof(Point3DCanvas), new PropertyMetadata(true));
            Point3DCanvas.ShowViewCubeProperty = DependencyProperty.Register(nameof(Point3DCanvas.ShowViewCube), typeof(bool), typeof(Point3DCanvas), new PropertyMetadata(true));
            Point3DCanvas.ShowCoordinateSystemProperty = DependencyProperty.Register(nameof(Point3DCanvas.ShowCoordinateSystem), typeof(bool), typeof(Point3DCanvas), new PropertyMetadata(true));
            Point3DCanvas.ShowCameraInfoProperty = DependencyProperty.Register(nameof(Point3DCanvas.ShowCameraInfo), typeof(bool), typeof(Point3DCanvas), new PropertyMetadata(true));
            Point3DCanvas.ModelUpDirectionProperty = DependencyProperty.Register(nameof(Point3DCanvas.ModelUpDirection), typeof(ThreeDUpDirection), typeof(Point3DCanvas), new PropertyMetadata(ThreeDUpDirection.Y, Point3DCanvas.ModelUpDirectionChanged));
            Point3DCanvas.CameraProperty = DependencyProperty.Register(nameof(Point3DCanvas.Camera), typeof(ThreeDCamera), typeof(Point3DCanvas), new PropertyMetadata(ThreeDConstants.UpDirectionZDefaultCamera, Point3DCanvas.OnCameraChanged));
            Point3DCanvas.Model3DProperty = DependencyProperty.Register(nameof(Point3DCanvas.Model3D), typeof(SceneNodeGroupModel3D), typeof(Point3DCanvas), new PropertyMetadata(null));
            Point3DCanvas.SizeXProperty = DependencyProperty.Register(nameof(Point3DCanvas.SizeX), typeof(double), typeof(Point3DCanvas), new PropertyMetadata(1d));
            Point3DCanvas.SizeYProperty = DependencyProperty.Register(nameof(Point3DCanvas.SizeY), typeof(double), typeof(Point3DCanvas), new PropertyMetadata(1d));
            Point3DCanvas.SizeZProperty = DependencyProperty.Register(nameof(Point3DCanvas.SizeZ), typeof(double), typeof(Point3DCanvas), new PropertyMetadata(1d));
            Point3DCanvas.ScaleXProperty = DependencyProperty.Register(nameof(Point3DCanvas.ScaleX), typeof(double), typeof(Point3DCanvas), new PropertyMetadata(1d, Point3DCanvas.OnScaleXChanged));
            Point3DCanvas.ScaleYProperty = DependencyProperty.Register(nameof(Point3DCanvas.ScaleY), typeof(double), typeof(Point3DCanvas), new PropertyMetadata(1d, Point3DCanvas.OnScaleYChanged));
            Point3DCanvas.ScaleZProperty = DependencyProperty.Register(nameof(Point3DCanvas.ScaleZ), typeof(double), typeof(Point3DCanvas), new PropertyMetadata(1d, Point3DCanvas.OnScaleZChanged));
            Point3DCanvas.OffsetXProperty = DependencyProperty.Register(nameof(Point3DCanvas.OffsetX), typeof(double), typeof(Point3DCanvas), new PropertyMetadata(0d));
            Point3DCanvas.OffsetYProperty = DependencyProperty.Register(nameof(Point3DCanvas.OffsetY), typeof(double), typeof(Point3DCanvas), new PropertyMetadata(0d));
            Point3DCanvas.OffsetZProperty = DependencyProperty.Register(nameof(Point3DCanvas.OffsetZ), typeof(double), typeof(Point3DCanvas), new PropertyMetadata(0d));
            Point3DCanvas.RotateAxisProperty = DependencyProperty.Register(nameof(Point3DCanvas.RotateAxis), typeof(Vector3D), typeof(Point3DCanvas), new PropertyMetadata(new Vector3D()));
            Point3DCanvas.RotateAngleProperty = DependencyProperty.Register(nameof(Point3DCanvas.RotateAngle), typeof(double), typeof(Point3DCanvas), new PropertyMetadata(0d));
            Point3DCanvas.FixedSizeProperty = DependencyProperty.Register(nameof(Point3DCanvas.FixedSize), typeof(bool), typeof(Point3DCanvas), new PropertyMetadata(true));

            // 注册路由事件
            Point3DCanvas._Load3dModelBeforEvent = EventManager.RegisterRoutedEvent(nameof(Point3DCanvas.Load3dModelBefor), RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(Point3DCanvas));
            Point3DCanvas._Load3dModelAfterEvent = EventManager.RegisterRoutedEvent(nameof(Point3DCanvas.Load3dModelAfter), RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(Point3DCanvas));
            Point3DCanvas._PointDrawnEvent = EventManager.RegisterRoutedEvent(nameof(Point3DCanvas.PointDrawn), RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(Point3DCanvas));
            Point3DCanvas._PointClickEvent = EventManager.RegisterRoutedEvent(nameof(Point3DCanvas.PointClick), RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(Point3DCanvas));

        }


        /// <summary>
        /// 构造器
        /// </summary>
        public Point3DCanvas()
        {
            this.InitializeComponent();

            // 默认值
            this._edit = false;
            this._firstElement3DIndex = 5;
            this._defaultCamera = new ThreeDCamera();

            // 重置工具栏操作模式组件
            this.ResetToolbarOperatorModeControl();

            // 重新加载操作模式
            this.ReloadOperatorMode();

            // 重新加载使用模式
            this.ReloadUseMode(this.UseMode);

            // 加载默认相机
            this.ReloadViewportModelUpDirection(this.ModelUpDirection);
            this.LoadDefaultViewportCamera();
            this.ReloadDefaultCamera(this.Camera);
        }
        #endregion

        #region # 依赖属性

        // 基础相关

        #region 鼠标位置 —— string MouseLocation
        /// <summary>
        /// 鼠标位置依赖属性
        /// </summary>
        public static DependencyProperty MouseLocationProperty;

        /// <summary>
        /// 鼠标位置
        /// </summary>
        public string MouseLocation
        {
            get => (string)base.GetValue(Point3DCanvas.MouseLocationProperty);
            set => base.SetValue(Point3DCanvas.MouseLocationProperty, value);
        }
        #endregion

        #region 文件路径 —— string FilePath
        /// <summary>
        /// 文件路径依赖属性
        /// </summary>
        public static DependencyProperty FilePathProperty;

        /// <summary>
        /// 文件路径
        /// </summary>
        public string FilePath
        {
            get => (string)base.GetValue(Point3DCanvas.FilePathProperty);
            set => base.SetValue(Point3DCanvas.FilePathProperty, value);
        }
        #endregion

        #region 使用模式 —— Point3DCanvasUseMode UseModel
        /// <summary>
        /// 使用模式依赖属性
        /// </summary>
        public static DependencyProperty UseModeProperty;

        /// <summary>
        /// 使用模式
        /// </summary>
        public CanvasUseMode UseMode
        {
            get => (CanvasUseMode)base.GetValue(Point3DCanvas.UseModeProperty);
            set => base.SetValue(Point3DCanvas.UseModeProperty, value);
        }
        #endregion

        #region 操作模式 —— Point3DCanvasOperationMode OperationMode
        /// <summary>
        /// 操作模式依赖属性
        /// </summary>
        public static DependencyProperty OperationModeProperty;

        /// <summary>
        /// 操作模式
        /// </summary>
        public CanvasOperatorMode OperationMode
        {
            get => (CanvasOperatorMode)base.GetValue(Point3DCanvas.OperationModeProperty);
            set => base.SetValue(Point3DCanvas.OperationModeProperty, value);
        }
        #endregion 

        #region 3D点优化模式 —— Point3DOptimizeMode PointOptimizeMode
        /// <summary>
        /// 3D点优化模式依赖属性
        /// </summary>
        public static DependencyProperty PointOptimizeModeProperty;

        /// <summary>
        /// 3D点优化模式
        /// </summary>
        public Point3DOptimizeMode PointOptimizeMode
        {
            get => (Point3DOptimizeMode)base.GetValue(Point3DCanvas.PointOptimizeModeProperty);
            set => base.SetValue(Point3DCanvas.PointOptimizeModeProperty, value);
        }
        #endregion 

        #region 颜色 —— Color Color
        /// <summary>
        /// 颜色
        /// </summary>
        public static DependencyProperty ColorProperty;

        /// <summary>
        /// 颜色
        /// </summary>
        public Color Color
        {
            get => (Color)base.GetValue(Point3DCanvas.ColorProperty);
            set => base.SetValue(Point3DCanvas.ColorProperty, value);
        }
        #endregion

        #region 点半径 —— double PointRadius
        /// <summary>
        /// 点半径依赖属性
        /// </summary>
        public static DependencyProperty PointRadiusProperty;

        /// <summary>
        /// 点半径
        /// </summary>
        public double PointRadius
        {
            get => (double)base.GetValue(Point3DCanvas.PointRadiusProperty);
            set => base.SetValue(Point3DCanvas.PointRadiusProperty, value);
        }
        #endregion

        #region 点列表 —— ObservableCollection<Point3DInfo> Points
        /// <summary>
        /// 点列表依赖属性
        /// </summary>
        public static DependencyProperty PointsProperty;

        /// <summary>
        /// 点列表
        /// </summary>
        public ObservableCollection<Point3DInfo> Points
        {
            get => (ObservableCollection<Point3DInfo>)base.GetValue(Point3DCanvas.PointsProperty);
            set => base.SetValue(Point3DCanvas.PointsProperty, value);
        }
        #endregion


        // 3D 视口相关

        #region 效果器 —— IEffectsManager EffectsManager
        /// <summary>
        /// 效果器依赖属性
        /// </summary>
        public static DependencyProperty EffectsManagerProperty;

        /// <summary>
        /// 效果器
        /// </summary>
        public IEffectsManager EffectsManager
        {
            get => (IEffectsManager)base.GetValue(Point3DCanvas.EffectsManagerProperty);
            set => base.SetValue(Point3DCanvas.EffectsManagerProperty, value);
        }
        #endregion

        #region 是否可旋转 —— bool IsRotationEnabled
        /// <summary>
        /// 是否可旋转依赖属性
        /// </summary>
        public static DependencyProperty IsRotationEnabledProperty;

        /// <summary>
        /// 是否可旋转
        /// </summary>
        public bool IsRotationEnabled
        {
            get => (bool)base.GetValue(Point3DCanvas.IsRotationEnabledProperty);
            set => base.SetValue(Point3DCanvas.IsRotationEnabledProperty, value);
        }
        #endregion

        #region 显示立方体视图 —— bool ShowViewCube
        /// <summary>
        /// 显示立方体视图依赖属性
        /// </summary>
        public static DependencyProperty ShowViewCubeProperty;

        /// <summary>
        /// 显示立方体视图
        /// </summary>
        public bool ShowViewCube
        {
            get => (bool)base.GetValue(Point3DCanvas.ShowViewCubeProperty);
            set => base.SetValue(Point3DCanvas.ShowViewCubeProperty, value);
        }
        #endregion

        #region 显示坐标系 —— bool ShowCoordinateSystem
        /// <summary>
        /// 显示坐标系依赖属性
        /// </summary>
        public static DependencyProperty ShowCoordinateSystemProperty;

        /// <summary>
        /// 显示坐标系
        /// </summary>
        public bool ShowCoordinateSystem
        {
            get => (bool)base.GetValue(Point3DCanvas.ShowCoordinateSystemProperty);
            set => base.SetValue(Point3DCanvas.ShowCoordinateSystemProperty, value);
        }
        #endregion

        #region 显示相机信息 —— bool ShowCameraInfo
        /// <summary>
        /// 显示相机信息依赖属性
        /// </summary>
        public static DependencyProperty ShowCameraInfoProperty;

        /// <summary>
        /// 显示相机信息
        /// </summary>
        public bool ShowCameraInfo
        {
            get => (bool)base.GetValue(Point3DCanvas.ShowCameraInfoProperty);
            set => base.SetValue(Point3DCanvas.ShowCameraInfoProperty, value);
        }
        #endregion 

        #region 模型向上方向 —— ThreeDUpDirection ModelUpDirection
        /// <summary>
        /// 模型向上方向依赖属性
        /// </summary>
        public static DependencyProperty ModelUpDirectionProperty;

        /// <summary>
        /// 模型向上方向
        /// </summary>
        public ThreeDUpDirection ModelUpDirection
        {
            get => (ThreeDUpDirection)base.GetValue(Point3DCanvas.ModelUpDirectionProperty);
            set => base.SetValue(Point3DCanvas.ModelUpDirectionProperty, value);
        }
        #endregion


        // 3D 相机视角相关

        #region 相机视角 —— ThreeDCamera CameraProperty
        /// <summary>
        /// 相机视角依赖属性
        /// </summary>
        public static DependencyProperty CameraProperty;

        /// <summary>
        /// 相机视角
        /// </summary>
        public ThreeDCamera Camera
        {
            get => (ThreeDCamera)base.GetValue(Point3DCanvas.CameraProperty);
            set => base.SetValue(Point3DCanvas.CameraProperty, value);
        }

        #endregion 


        // 3D 模型相关

        #region 3D数模 —— SceneNodeGroupModel3D Model3D
        /// <summary>
        /// 3D数模依赖属性
        /// </summary>
        public static DependencyProperty Model3DProperty;

        /// <summary>
        /// 3D数模
        /// </summary>
        public SceneNodeGroupModel3D Model3D
        {
            get => (SceneNodeGroupModel3D)base.GetValue(Point3DCanvas.Model3DProperty);
            set => base.SetValue(Point3DCanvas.Model3DProperty, value);
        }
        #endregion

        #region 数模大小X —— double SizeX
        /// <summary>
        /// 数模大小X依赖属性
        /// </summary>
        public static DependencyProperty SizeXProperty;

        /// <summary>
        /// 数模大小X
        /// </summary>
        public double SizeX
        {
            get => (double)base.GetValue(Point3DCanvas.SizeXProperty);
            set => base.SetValue(Point3DCanvas.SizeXProperty, value);
        }
        #endregion

        #region 数模大小Y —— double SizeY
        /// <summary>
        /// 数模大小Y依赖属性
        /// </summary>
        public static DependencyProperty SizeYProperty;

        /// <summary>
        /// 数模大小Y
        /// </summary>
        public double SizeY
        {
            get => (double)base.GetValue(Point3DCanvas.SizeYProperty);
            set => base.SetValue(Point3DCanvas.SizeYProperty, value);
        }
        #endregion

        #region 数模大小Z —— double SizeZ
        /// <summary>
        /// 数模大小Z依赖属性
        /// </summary>
        public static DependencyProperty SizeZProperty;

        /// <summary>
        /// 数模大小Z
        /// </summary>
        public double SizeZ
        {
            get => (double)base.GetValue(Point3DCanvas.SizeZProperty);
            set => base.SetValue(Point3DCanvas.SizeZProperty, value);
        }
        #endregion

        #region 缩放比例X —— double ScaleX
        /// <summary>
        /// 缩放比例X依赖属性
        /// </summary>
        public static DependencyProperty ScaleXProperty;

        /// <summary>
        /// 缩放比例X
        /// </summary>
        public double ScaleX
        {
            get => (double)base.GetValue(Point3DCanvas.ScaleXProperty);
            set => base.SetValue(Point3DCanvas.ScaleXProperty, value);
        }
        #endregion

        #region 缩放比例Y —— double ScaleY
        /// <summary>
        /// 缩放比例Y依赖属性
        /// </summary>
        public static DependencyProperty ScaleYProperty;

        /// <summary>
        /// 缩放比例Y
        /// </summary>
        public double ScaleY
        {
            get => (double)base.GetValue(Point3DCanvas.ScaleYProperty);
            set => base.SetValue(Point3DCanvas.ScaleYProperty, value);
        }
        #endregion

        #region 缩放比例Z —— double ScaleZ
        /// <summary>
        /// 缩放比例Z依赖属性
        /// </summary>
        public static DependencyProperty ScaleZProperty;

        /// <summary>
        /// 缩放比例Z
        /// </summary>
        public double ScaleZ
        {
            get => (double)base.GetValue(Point3DCanvas.ScaleZProperty);
            set => base.SetValue(Point3DCanvas.ScaleZProperty, value);
        }
        #endregion

        #region X轴补偿 —— double OffsetX
        /// <summary>
        /// X轴补偿依赖属性
        /// </summary>
        public static DependencyProperty OffsetXProperty;

        /// <summary>
        /// X轴补偿
        /// </summary>
        public double OffsetX
        {
            get => (double)base.GetValue(Point3DCanvas.OffsetXProperty);
            set => base.SetValue(Point3DCanvas.OffsetXProperty, value);
        }
        #endregion

        #region Y轴补偿 —— double OffsetY
        /// <summary>
        /// Y轴补偿依赖属性
        /// </summary>
        public static DependencyProperty OffsetYProperty;

        /// <summary>
        /// Y轴补偿
        /// </summary>
        public double OffsetY
        {
            get => (double)base.GetValue(Point3DCanvas.OffsetYProperty);
            set => base.SetValue(Point3DCanvas.OffsetYProperty, value);
        }
        #endregion

        #region Z轴补偿 —— double OffsetZ
        /// <summary>
        /// Z轴补偿依赖属性
        /// </summary>
        public static DependencyProperty OffsetZProperty;

        /// <summary>
        /// Z轴补偿
        /// </summary>
        public double OffsetZ
        {
            get => (double)base.GetValue(Point3DCanvas.OffsetZProperty);
            set => base.SetValue(Point3DCanvas.OffsetZProperty, value);
        }
        #endregion

        #region 旋转轴 —— Vector3D RotateAxis
        /// <summary>
        /// 旋转轴依赖属性
        /// </summary>
        public static DependencyProperty RotateAxisProperty;

        /// <summary>
        /// 旋转轴
        /// </summary>
        public Vector3D RotateAxis
        {
            get => (Vector3D)base.GetValue(Point3DCanvas.RotateAxisProperty);
            set => base.SetValue(Point3DCanvas.RotateAxisProperty, value);
        }
        #endregion

        #region 旋转角度 —— double RotateAngle
        /// <summary>
        /// 旋转角度依赖属性
        /// </summary>
        public static DependencyProperty RotateAngleProperty;

        /// <summary>
        /// 旋转角度
        /// </summary>
        public double RotateAngle
        {
            get => (double)base.GetValue(Point3DCanvas.RotateAngleProperty);
            set => base.SetValue(Point3DCanvas.RotateAngleProperty, value);
        }
        #endregion


        // 3D 元素相关

        #region 固定大小 —— bool FixedSize
        /// <summary>
        /// 固定大小依赖属性
        /// </summary>
        public static DependencyProperty FixedSizeProperty;

        /// <summary>
        /// 固定大小
        /// </summary>
        public bool FixedSize
        {
            get => (bool)base.GetValue(Point3DCanvas.FixedSizeProperty);
            set => base.SetValue(Point3DCanvas.FixedSizeProperty, value);
        }
        #endregion

        #endregion

        #region # 回调方法

        #region 文件路径改变回调方法 —— static async Task OnFilePathChanged(DependencyObject dependencyObject...
        /// <summary>
        /// 文件路径改变回调方法
        /// </summary>
        private static async void OnFilePathChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Point3DCanvas that = (Point3DCanvas)dependencyObject;

            // 重新加载3D数模
            await that.Reload3dModel();

        }
        #endregion

        #region 颜色改变回调方法 —— static async Task OnColorChanged(DependencyObject dependencyObject...
        /// <summary>
        /// 颜色改变回调方法
        /// </summary>
        private static async void OnColorChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Point3DCanvas that = (Point3DCanvas)dependencyObject;

            // 重新加载3D数模材质
            await that.Reload3dModelMaterial();

        }
        #endregion

        #region 使用模式改变回调方法 —— static void OnUseModelChanged(DependencyObject dependencyObject...
        /// <summary>
        /// 使用模式改变回调方法
        /// </summary>
        private static void OnUseModelChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Point3DCanvas that = (Point3DCanvas)dependencyObject;
            CanvasUseMode oldValue = (CanvasUseMode)eventArgs.OldValue;
            CanvasUseMode newValue = (CanvasUseMode)eventArgs.NewValue;

            that.ReloadUseMode(newValue);
        }
        #endregion

        #region 操作模式改变回调方法 —— static void OnOperatorModelChanged(DependencyObject dependencyObject...
        /// <summary>
        /// 操作模式改变回调方法
        /// </summary>
        private static void OnOperatorModelChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Point3DCanvas that = (Point3DCanvas)dependencyObject;
            CanvasOperatorMode newValue = (CanvasOperatorMode)eventArgs.NewValue;

            // 重置其他按钮状态
            that.ResetToolbarOperatorModeControl();

            that.ReloadOperatorMode();
        }
        #endregion

        #region 操作模式改变回调方法 —— static void OnPoint3DOptimizeModeChanged(DependencyObject dependencyObject...
        /// <summary>
        /// 操作模式改变回调方法
        /// </summary>
        private static void OnPoint3DOptimizeModeChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Point3DCanvas that = (Point3DCanvas)dependencyObject;
            CanvasOperatorMode newValue = (CanvasOperatorMode)eventArgs.NewValue;

            if (!that._isFirstRender)
                return;

            // TODO 重新绘制
        }
        #endregion 

        #region 模型向上方向改变回调方法 —— static void ModelUpDirectionChanged(DependencyObject dependencyObject...
        /// <summary>
        /// 模型向上方向改变回调方法
        /// </summary>
        private static void ModelUpDirectionChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Point3DCanvas that = (Point3DCanvas)dependencyObject;
            ThreeDUpDirection newValue = (ThreeDUpDirection)eventArgs.NewValue;


            that.ReloadViewportModelUpDirection(newValue);

            ThreeDCamera defaultCamera = ThreeDService.GetDefaultCamera(newValue);
            that.ReloadViewportCamera(defaultCamera);
        }
        #endregion

        #region 相机视角改变回调方法 —— static void OnCameraChanged(DependencyObject dependencyObject...
        /// <summary>
        /// 相机视角改变回调方法
        /// </summary>
        private static void OnCameraChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Point3DCanvas that = (Point3DCanvas)dependencyObject;
            ThreeDCamera newValue = (ThreeDCamera)eventArgs.NewValue;

            if (newValue == null)
                return;

            that.ReloadViewportCamera(newValue);
            that.ReloadDefaultCamera(newValue);
        }
        #endregion

        #region 缩放比例X改变回调方法 —— static void OnScaleXChanged(object sender...
        /// <summary>
        /// 缩放比例X改变回调方法
        /// </summary>
        private static void OnScaleXChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Point3DCanvas that = (Point3DCanvas)dependencyObject;
            double oldValue = (double)eventArgs.OldValue;
            double newValue = (double)eventArgs.NewValue;

            that.Reload3dModelOffset();
        }
        #endregion

        #region 缩放比例Y改变回调方法 —— static void OnScaleYChanged(object sender...
        /// <summary>
        /// 缩放比例Y改变回调方法
        /// </summary>
        private static void OnScaleYChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Point3DCanvas that = (Point3DCanvas)dependencyObject;
            double oldValue = (double)eventArgs.OldValue;
            double newValue = (double)eventArgs.NewValue;

            that.Reload3dModelOffset();
        }
        #endregion

        #region 缩放比例Z改变回调方法 —— static void OnScaleZChanged(object sender...
        /// <summary>
        /// 缩放比例Z改变回调方法
        /// </summary>
        private static void OnScaleZChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Point3DCanvas that = (Point3DCanvas)dependencyObject;
            double oldValue = (double)eventArgs.OldValue;
            double newValue = (double)eventArgs.NewValue;

            that.Reload3dModelOffset();
        }
        #endregion

        #region 点半径改变回调方法 —— static void OnPointRadiusChanged(DependencyObject dependencyObject...
        /// <summary>
        /// 点半径改变回调方法
        /// </summary>
        private static void OnPointRadiusChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Point3DCanvas that = (Point3DCanvas)dependencyObject;

            that.RedrawPoints(that.Points);
        }
        #endregion

        #region 点列表改变回调方法 —— static void OnPointsChanged(DependencyObject dependencyObject...
        /// <summary>
        /// 点列表改变回调方法
        /// </summary>
        private static void OnPointsChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            Point3DCanvas that = (Point3DCanvas)dependencyObject;

            if (that.Points == null || that._edit)
                return;

            that.Points.CollectionChanged += that.OnPointsItemsChanged;
            that.RedrawPoints(that.Points);
        }
        #endregion

        #region 点列表元素改变回调方法 —— void OnPointsItemsChanged(object sender...
        /// <summary>
        /// 点列表元素改变回调方法
        /// </summary>
        private void OnPointsItemsChanged(object sender, NotifyCollectionChangedEventArgs eventArgs)
        {
            if (this._edit)
                return;

            ObservableCollection<Point3DInfo> points = (ObservableCollection<Point3DInfo>)sender;
            this.RedrawPoints(points);
        }
        #endregion

        #endregion

        #region # 路由事件

        #region 3D数模文件加载前路由事件 —— RoutedEvent Load3dModelBefor
        /// <summary>
        /// 3D数模文件加载前路由事件
        /// </summary>
        private static readonly RoutedEvent _Load3dModelBeforEvent;

        /// <summary>
        /// 3D数模文件加载前路由事件处理程序
        /// </summary>
        public event RoutedEventHandler Load3dModelBefor
        {
            add => base.AddHandler(Point3DCanvas._Load3dModelBeforEvent, value);
            remove => base.RemoveHandler(Point3DCanvas._Load3dModelBeforEvent, value);
        }
        #endregion

        #region 3D数模文件加载后路由事件 —— RoutedEvent Load3dModelAfter
        /// <summary>
        /// 3D数模文件加载后路由事件
        /// </summary>
        private static readonly RoutedEvent _Load3dModelAfterEvent;

        /// <summary>
        /// 3D数模文件加载后路由事件处理程序
        /// </summary>
        public event RoutedEventHandler Load3dModelAfter
        {
            add => base.AddHandler(Point3DCanvas._Load3dModelAfterEvent, value);
            remove => base.RemoveHandler(Point3DCanvas._Load3dModelAfterEvent, value);
        }
        #endregion

        #region 点位置绘制完成路由事件 —— RoutedEvent PointDrawn
        /// <summary>
        /// 点位置绘制完成路由事件
        /// </summary>
        private static readonly RoutedEvent _PointDrawnEvent;

        /// <summary>
        /// 点位置绘制完成路由事件处理程序
        /// </summary>
        public event RoutedEventHandler PointDrawn
        {
            add => base.AddHandler(Point3DCanvas._PointDrawnEvent, value);
            remove => base.RemoveHandler(Point3DCanvas._PointDrawnEvent, value);
        }
        #endregion

        #region 点点击路由事件 —— RoutedEvent PointClick
        /// <summary>
        /// 点点击路由事件
        /// </summary>
        private static readonly RoutedEvent _PointClickEvent;

        /// <summary>
        /// 点点击路由事件处理程序
        /// </summary>
        public event RoutedEventHandler PointClick
        {
            add => base.AddHandler(Point3DCanvas._PointClickEvent, value);
            remove => base.RemoveHandler(Point3DCanvas._PointClickEvent, value);
        }
        #endregion

        #endregion

        #region # 事件处理程序

        #region 打开文件点击事件 —— void OpenFileClick(object sender...
        /// <summary>
        /// 打开文件点击事件
        /// </summary>
        private void OpenFileClick(object sender, RoutedEventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog
            {
                Filter = "3D数模文件(*.stl)|*.stl|所有文件|*",
                AddExtension = true,
                RestoreDirectory = true
            };

            if (openFileDialog.ShowDialog() != true)
                return;

            this.FilePath = openFileDialog.FileName;
        }
        #endregion

        #region 重置视角点击事件 —— void ReloadViewingAngleClick(object sender...
        /// <summary>
        /// 重置视角点击事件
        /// </summary>
        private void ReloadViewingAngleClick(object sender, RoutedEventArgs e)
        {
            this.ReloadViewportCamera(this._defaultCamera);
        }
        #endregion

        #region 工具栏操作模式点击事件 —— void ToolbarOperatorModelClick(object sender...
        /// <summary>
        /// 工具栏操作模式点击事件
        /// </summary>
        private void ToolbarOperatorModelClick(object sender, RoutedEventArgs e)
        {
            ToggleButton tbtn = (ToggleButton)sender;

            // 重置其他按钮状态
            this.ResetToolbarOperatorModeControl();


            if (tbtn == this.BtnNormal)
            {
                this.OperationMode = CanvasOperatorMode.Nomal;
            }
            else if (tbtn == this.BtnPoint)
            {
                this.OperationMode = CanvasOperatorMode.Draw;
            }

            // 设置选中
            tbtn.IsChecked = true;
        }
        #endregion

        #region 3D视口鼠标左键按下事件 —— void ViewportMouseLeftDown(Viewport3DX viewport3D...
        /// <summary>
        /// 鼠标左键按下事件
        /// </summary>
        private void ViewportMouseLeftDown(object sender, MouseButtonEventArgs eventArgs)
        {
            Viewport3DX viewport3D = (Viewport3DX)sender;
            Point mousePos2D = eventArgs.GetPosition(viewport3D);
            bool success = viewport3D.FindNearest(mousePos2D, out System.Windows.Media.Media3D.Point3D mousePos3D, out Vector3D normal, out Element3D element3D, out SceneNode sceneNode);

            if (!success)
                return;

            switch (this.OperationMode)
            {
                case CanvasOperatorMode.Draw:
                    this._edit = true;

                    Point3DInfo point = this.DrawPoint(mousePos3D, element3D, normal);

                    if (point != null)
                    {
                        Point3DDrawnEventArgs args = new Point3DDrawnEventArgs(Point3DCanvas._PointDrawnEvent, this, point);
                        this.RaiseEvent(args);

                        this.Points.Add(point);
                    }

                    break;
                case CanvasOperatorMode.Nomal:
                case CanvasOperatorMode.Drag:
                default:
                    break;
            }

        }
        #endregion

        #region 3D视口鼠标右键按下事件 —— void ViewportMouseRightDown(Viewport3DX viewport3D...
        /// <summary>
        /// 鼠标右键按下事件
        /// </summary>
        private void ViewportMouseRightDown(object sender, MouseButtonEventArgs eventArgs)
        {
            Viewport3DX viewport3D = (Viewport3DX)sender;
            Point mousePos2D = eventArgs.GetPosition(viewport3D);
            bool success = viewport3D.FindNearest(mousePos2D, out System.Windows.Media.Media3D.Point3D mousePos3D, out Vector3D normal, out Element3D element3D, out SceneNode sceneNode);

        }
        #endregion

        #region 3D视口鼠标松开事件 —— void ViewportMouseUp(Viewport3DX viewport3D...
        /// <summary>
        /// 鼠标松开事件
        /// </summary>
        private void ViewportMouseUp(object sender, MouseButtonEventArgs eventArgs)
        {
            Viewport3DX viewport3D = (Viewport3DX)sender;

            switch (this.OperationMode)
            {
                case CanvasOperatorMode.Nomal:
                default:
                    break;
            }

            this._edit = false;
            this._selectedElement3D = null;
        }
        #endregion

        #region 3D视口鼠标移动事件 —— void ViewportMouseMove(Viewport3DX viewport3D...
        /// <summary>
        /// 鼠标移动事件
        /// </summary>
        private async void ViewportMouseMove(object sender, MouseEventArgs eventArgs)
        {
            Viewport3DX viewport3D = (Viewport3DX)sender;
            Point mousePos2D = eventArgs.GetPosition(viewport3D);
            bool success = viewport3D.FindNearest(mousePos2D, out System.Windows.Media.Media3D.Point3D mousePos3D, out Vector3D normal, out Element3D element3D, out SceneNode sceneNode);
            this.MouseLocation = $"（{mousePos3D}）";


            // 重新加载鼠标指针
            this.ReloadCursor(success, element3D);

            // 鼠标经过点
            this.MouseOverPoint(element3D);

            switch (this.OperationMode)
            {
                case CanvasOperatorMode.Nomal:
                default:
                    break;
            }
        }
        #endregion

        #region 3D视口相机改变事件 —— void ViewportCameraChanged(object sender, RoutedEventArgs e)
        /// <summary>
        /// 3D视口相机改变事件
        /// </summary>
        private void ViewportCameraChanged(object sender, RoutedEventArgs e)
        {
            if (this.Viewport3Dx.Camera == null)
                return;

            this.Camera.LookDirection = this.Viewport3Dx.Camera.LookDirection;
            this.Camera.Position = this.Viewport3Dx.Camera.Position;
            this.Camera.UpDirection = this.Viewport3Dx.Camera.UpDirection;

            this.Light3D.Direction = this.Camera.LookDirection;
        }
        #endregion

        #region 3D视口渲染完毕事件 —— void OnViewportRendered(object sender, System.EventArgs e)
        /// <summary>
        /// 3D视口渲染完毕事件
        /// </summary>
        private void OnViewportRendered(object sender, System.EventArgs e)
        {
            if (this._isFirstRender)
                return;

            if (this._sceneRoot == null)
                return;

            this._isFirstRender = true;

            ThreeDLoadFileAfterEventArgs argAfters = new ThreeDLoadFileAfterEventArgs(Point3DCanvas._Load3dModelAfterEvent, this);
            this.RaiseEvent(argAfters);
        }
        #endregion

        #endregion

        #region # 方法

        // Public 

        #region 截图 —— async Task PrintScreenToFile(string filePath)
        /// <summary>
        /// 截图
        /// </summary>
        public async Task PrintScreenToFile(string filePath)
        {
            await Task.Run(() =>
            {
                string rootPath = Path.GetDirectoryName(filePath);
                if (rootPath == null)
                    throw new Exception("文件路径获取失败！");

                if (!Directory.Exists(rootPath))
                {
                    Directory.CreateDirectory(rootPath);
                }

                this.Viewport3Dx.SaveScreen(filePath);

            });
        }
        #endregion

        #region 截图 —— async Task PrintScreenToBytes()
        /// <summary>
        /// 截图
        /// </summary>
        public async Task<byte[]> PrintScreenToBytes()
        {
            return await Task.Run(() =>
            {
                using MemoryStream memoryStream = this.Viewport3Dx.RenderToBitmapStream();

                return memoryStream.ToArray();
            });
        }
        #endregion


        // Private

        #region 加载3D视口默认相机 —— void LoadDefaultViewportCamera()
        /// <summary>
        /// 加载3D视口默认相机
        /// </summary>
        private void LoadDefaultViewportCamera()
        {
            ThreeDCamera defaultCamera = ThreeDService.GetDefaultCamera(this.ModelUpDirection);

            PerspectiveCamera camera = (PerspectiveCamera)this.Viewport3Dx.Camera;
            camera.LookDirection = defaultCamera.LookDirection;
            camera.UpDirection = defaultCamera.UpDirection;
            camera.Position = defaultCamera.Position;
            camera.NearPlaneDistance = 0.125;
            camera.FarPlaneDistance = double.PositiveInfinity;
            camera.FieldOfView = 30;
        }
        #endregion

        #region 重新加载3D视口模型向上方向 —— void ReloadViewportModelUpDirection()
        /// <summary>
        /// 重新加载3D视口模型向上方向
        /// </summary>
        private void ReloadViewportModelUpDirection(ThreeDUpDirection threeDUpDirection)
        {
            this.Viewport3Dx.ModelUpDirection = threeDUpDirection switch
            {
                ThreeDUpDirection.X => new Vector3D(1, 0, 0),
                ThreeDUpDirection.Y => new Vector3D(0, 1, 0),
                ThreeDUpDirection.Z => new Vector3D(0, 0, 1),
                _ => new Vector3D(0, 1, 0),
            };
        }
        #endregion

        #region 重置工具栏操作模式组件 —— void ResetToolbarOperatorModeControl()
        /// <summary>
        /// 重置工具栏操作模式组件
        /// </summary>
        private void ResetToolbarOperatorModeControl()
        {
            this.BtnNormal.IsChecked = false;
            this.BtnPoint.IsChecked = false;

            if (this.BtnPoint.IsChecked.HasValue && this.BtnPoint.IsChecked.Value)
            {
                this._tempPointsVisual3D.Points.Clear();
            }

        }
        #endregion

        #region 重新加载栏操作模式 —— void ReloadOperatorMode()
        /// <summary>
        /// 重新加载栏操作模式
        /// </summary>
        private void ReloadOperatorMode()
        {
            switch (this.OperationMode)
            {
                case CanvasOperatorMode.Nomal:
                    this.BtnNormal.IsChecked = true;
                    break;
            }
        }
        #endregion

        #region 重新加载使用模式 —— void ReloadUseMode(CanvasUseMode newUseModel)
        /// <summary>
        /// 重新加载使用模式
        /// </summary>
        /// <param name="useModel">使用模式</param>
        private void ReloadUseMode(CanvasUseMode useModel)
        {
            this.BtnResetViewAngle.Visibility = Visibility.Visible;
            this.BtnNormal.Visibility = Visibility.Visible;
            this.BtnOpenFile.Visibility = Visibility.Collapsed;
            this.BtnPoint.Visibility = Visibility.Collapsed;

            switch (useModel)
            {
                case CanvasUseMode.Edit:
                    this.BtnPoint.Visibility = Visibility.Visible;
                    this.BtnOpenFile.Visibility = Visibility.Visible;
                    break;
                default:
                case CanvasUseMode.Nomal:

                    break;
            }
        }
        #endregion

        #region 重新加载相机 —— void ReloadDefaultCamera(ThreeDCamera camera)
        /// <summary>
        /// 重新加载相机
        /// </summary>
        private void ReloadDefaultCamera(ThreeDCamera camera)
        {
            this._defaultCamera.UpDirection = camera.UpDirection;
            this._defaultCamera.Position = camera.Position;
            this._defaultCamera.LookDirection = camera.LookDirection;
        }
        #endregion

        #region 重新加载3D视口相机 —— void ReloadViewportCamera(ThreeDCamera camera)
        /// <summary>
        /// 重新加载3D视口相机
        /// </summary>
        private void ReloadViewportCamera(ThreeDCamera camera)
        {
            this.Viewport3Dx.Camera.LookDirection = camera.LookDirection;
            this.Viewport3Dx.Camera.UpDirection = camera.UpDirection;
            this.Viewport3Dx.Camera.Position = camera.Position;
        }
        #endregion

        #region 重新加载3D数模 ——  void Reload3dModel()
        /// <summary>
        /// 重新加载3D数模
        /// </summary>
        private async Task Reload3dModel()
        {
            ThreeDLoadFileBeforEventArgs argBefors = new ThreeDLoadFileBeforEventArgs(Point3DCanvas._Load3dModelBeforEvent, this);
            this.RaiseEvent(argBefors);

            this._isFirstRender = false;

            string filePath = this.FilePath;

            if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
            {
                this.Model3D?.Clear();
                return;
            }

            using Importer importer = new Importer();


            HelixToolkitScene scene = await Task.Run(() => importer.Load(filePath));
            this._sceneRoot = scene.Root;

            //加载数模
            this.Model3D = new SceneNodeGroupModel3D();
            this.Model3D.AddNode(this._sceneRoot);

            // 重新加载3D数模补偿信息
            this.Reload3dModelOffset();

            // 重新加载3D数模材质
            await this.Reload3dModelMaterial();
        }
        #endregion

        #region 重新加载3D数模补偿信息 —— void Reload3dModelOffset()
        /// <summary>
        /// 重新加载3D数模补偿信息
        /// </summary>
        private void Reload3dModelOffset()
        {
            if (this._sceneRoot == null)
                return;

            BoundingBox bounds = this.CalculateModelBounds(this._sceneRoot, SharpDX.Matrix.Identity);

            this.OffsetX = 0 - Math.Abs(bounds.Center.X) * this.ScaleX;
            this.OffsetY = 0 - Math.Abs(bounds.Center.Y) * this.ScaleY;
            this.OffsetZ = 0 - Math.Abs(bounds.Center.Z) * this.ScaleZ;

            this.Viewport3Dx.ZoomExtents(0);

            this.Viewport3Dx.FrontView();

            this.ReloadDefaultCamera(this.Camera);

            this.SizeX = bounds.Size.X * this.ScaleX;
            this.SizeY = bounds.Size.Y * this.ScaleY;
            this.SizeZ = bounds.Size.Z * this.ScaleZ;
        }
        #endregion

        #region 计算3D数模界限 —— BoundingBox CalculateModelBounds(SceneNode node...
        /// <summary>
        /// 计算3D数模界限
        /// </summary>
        /// <param name="node">节点</param>
        /// <param name="parentTransform">父节点矩阵</param>
        /// <returns>界限</returns>
        private BoundingBox CalculateModelBounds(SceneNode node, SharpDX.Matrix parentTransform)
        {
            BoundingBox bounds = new BoundingBox();
            SharpDX.Matrix nodeTransform = node.ModelMatrix * parentTransform;

            if (node is MeshNode meshNode)
            {
                MeshGeometry3D mesh = meshNode.Geometry as MeshGeometry3D;
                if (mesh?.Positions != null)
                {
                    foreach (Vector3 pos in mesh.Positions)
                    {
                        Vector3 transformedPos = Vector3.TransformCoordinate(pos, nodeTransform);
                        bounds = BoundingBox.Merge(bounds, new BoundingBox(transformedPos, transformedPos));
                    }
                }
            }

            foreach (SceneNode child in node.Items)
            {
                BoundingBox childBounds = this.CalculateModelBounds(child, nodeTransform);
                bounds = BoundingBox.Merge(bounds, childBounds);
            }

            return bounds;
        }
        #endregion

        #region 重新加载3D数模材质 —— void Reload3dModelMaterial()
        /// <summary>
        /// 重新加载3D数模材质
        /// </summary>
        private async Task Reload3dModelMaterial()
        {
            Color color = this.Color;

            IEnumerable<SceneNode> sceneNodes = this.Model3D.GroupNode.Traverse();

            await Task.Run(() =>
            {
                foreach (SceneNode sceneNode in sceneNodes)
                {
                    if (!(sceneNode is MaterialGeometryNode materialGeometryNode))
                        continue;

                    PhongMaterial phongMaterial = new PhongMaterial
                    {
                        DiffuseColor = Color.FromArgb(255, color.R, color.G, color.B).ToColor4(),
                        SpecularColor = Color.FromScRgb(1f, 0.005f, 0.005f, 0.005F).ToColor4()
                    };

                    materialGeometryNode.Material = phongMaterial;

                }
            });

        }
        #endregion

        #region 重新加载鼠标指针 —— void ReloadCursor(bool findNearestSuccess, Element3D element3D)
        /// <summary>
        /// 重新加载鼠标指针
        /// </summary>
        /// <param name="findNearestSuccess">查找边缘是否成功</param>
        /// <param name="element3D">3D元素</param>
        private void ReloadCursor(bool findNearestSuccess, Element3D element3D)
        {
            if (!findNearestSuccess)
            {
                if (Mouse.OverrideCursor != null)
                    Mouse.OverrideCursor = null;
                return;
            }

            if (element3D == null)
            {
                switch (this.OperationMode)
                {
                    case CanvasOperatorMode.Nomal:
                    case CanvasOperatorMode.Drag:
                    default:
                        break;
                    case CanvasOperatorMode.Draw:
                        Mouse.OverrideCursor = Cursors.Cross;
                        break;
                }
            }
            else if (element3D.GetType() == typeof(BillboardTextModel3D))
            {
                switch (this.OperationMode)
                {
                    case CanvasOperatorMode.Nomal:
                    default:
                        Mouse.OverrideCursor = Cursors.Hand;
                        break;
                    case CanvasOperatorMode.Draw:
                        break;
                    case CanvasOperatorMode.Drag:
                        Mouse.OverrideCursor = Cursors.SizeAll;
                        break;
                }
            }

        }
        #endregion

        #region 清理自定义3D元素 —— void ClearCustomerElement3D()
        /// <summary>
        /// 清理自定义3D元素
        /// </summary>
        private void ClearCustomerElement3D()
        {
            while (true)
            {
                if (this._firstElement3DIndex >= this.Viewport3Dx.Items.Count)
                    return;

                Application.Current.Dispatcher.Invoke(() =>
                {
                    this.Viewport3Dx.Items.RemoveAt(this._firstElement3DIndex);
                });
            }
        }
        #endregion

        #region 重新绘制点列表 —— void RedrawPoints(ObservableCollection<Point3DInfo> points)
        /// <summary>
        /// 重新绘制点列表
        /// </summary>
        private void RedrawPoints(ObservableCollection<Point3DInfo> points)
        {
            this.ClearCustomerElement3D();

            foreach (Point3DInfo point in points)
            {
                this.DrawPoint(point);
            }
        }
        #endregion

        #region 绘制点 —— SphereVisual3D DrawPoint(Point3DInfo point)
        /// <summary>
        /// 绘制点
        /// </summary>
        /// <param name="point">点信息</param> 
        private SphereVisual3D DrawPoint(Point3DInfo point)
        {

            Point3D location = point.Location;

            if (!this._edit && this.PointOptimizeMode == Point3DOptimizeMode.Hit)
            {
                List<HitTestResult> hitResults = new List<HitTestResult>();

                this.Viewport3Dx.HitTest(this._sceneRoot, point.Location, ref hitResults);

                location = hitResults.Nearest() ?? location;
            }

            SphereVisual3D pointsVisual3D = new SphereVisual3D
            {
                Radius = this.PointRadius,
                Material = PhongMaterials.Red,
                Transform = location.ToTranslateTransform3D()
            };

            pointsVisual3D.MouseDown3D += (sender, e) =>
            {
                Mouse.OverrideCursor = Cursors.Arrow;
                Point3DClickEventArgs eventArgs = new Point3DClickEventArgs(Point3DCanvas._PointClickEvent, this, point);
                this.RaiseEvent(eventArgs);
            };

            this.Viewport3Dx.Items.Add(pointsVisual3D);

            return pointsVisual3D;
        }
        #endregion

        #region 绘制点 —— Point3DInfo DrawPoint(Point3D position, Element3D element3D...
        /// <summary>
        /// 绘制点
        /// </summary> 
        /// <param name="position">位置</param>
        /// <param name="element3D">3D节点</param>
        /// <param name="normal">法向量</param>
        private Point3DInfo DrawPoint(Point3D position, Element3D element3D, Vector3D normal)
        {
            if (element3D != null)
                return null;

            Point3D point3D = new Point3D(position.X, position.Y, position.Z);

            Point3DInfo point3DInfo = new Point3DInfo("", point3D);
            point3DInfo.Visual3D = this.DrawPoint(point3DInfo);

            return point3DInfo;
        }
        #endregion

        #region 鼠标经过点 —— void MouseOverPoint(Element3D element3D)
        /// <summary>
        /// 鼠标经过点
        /// </summary> 
        /// <param name="element3D">3D节点</param>
        private void MouseOverPoint(Element3D element3D)
        {
            SphereVisual3D sphereVisual3D = (SphereVisual3D)this._selectedElement3D;

            if (element3D != null && element3D.GetType() == typeof(SphereVisual3D))
            {
                if (sphereVisual3D != null && sphereVisual3D != (SphereVisual3D)element3D)
                {
                    sphereVisual3D.Material = PhongMaterials.Red;
                }

                sphereVisual3D = (SphereVisual3D)element3D;
                sphereVisual3D.Material = PhongMaterials.Yellow;
            }
            else if (element3D == null && sphereVisual3D != null)
            {
                sphereVisual3D.Material = PhongMaterials.Red;
                sphereVisual3D = null;
            }

            this._selectedElement3D = sphereVisual3D;
        }
        #endregion

        #endregion

    }
}